#include "object_co_json.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "cjson/cJSON.h"
// #include "util/hex_util.h"

#define printf(fmt, ...) //fprintf(stdout, fmt, ##__VA_ARGS__)
int object_co_json (const char *name, void *obj, void *obj_desc_ptr, int32_t obj_field_count, void *json_obj, int depth) ;

int filed_to_json(st_obj_desc_t *info, void* value, cJSON **json_ptr, int depth, int arr_count) {
    printf("%s:%d name=%s type=%d arr_count=%d\n", __func__, __LINE__, info->name, info->type, arr_count);
    int ret = 0;
    cJSON *jsonObj = NULL;
    cJSON *arrayObj = NULL;
    cJSON *itemObj = NULL;
    st_obj_desc_t sub_info;
    int count = 0;
    void *sub_obj = NULL;
    switch (info->type)
    {
        case E_OFT_STRING:
            *json_ptr = cJSON_CreateString((char *)value);
            break;
        case E_OFT_INT8:
            *json_ptr = cJSON_CreateNumber(*(int8_t *)value);
            break;
        case E_OFT_UINT8:
            *json_ptr = cJSON_CreateNumber(*(uint8_t *)value);
            break;
        case E_OFT_INT16:
            *json_ptr = cJSON_CreateNumber(*(int16_t *)value);
            break;
        case E_OFT_UINT16:
            *json_ptr = cJSON_CreateNumber(*(uint16_t *)value);
            break;
        case E_OFT_INT32:
            *json_ptr = cJSON_CreateNumber(*(int32_t *)value);
            break;
        case E_OFT_UINT32:
            *json_ptr = cJSON_CreateNumber(*(uint32_t *)value);
            break;
        case E_OFT_INT64:
            *json_ptr = cJSON_CreateNumber(*(int64_t *)value);
            break;
        case E_OFT_UINT64:
            *json_ptr = cJSON_CreateNumber(*(uint64_t *)value);
            break;
        case E_OFT_FLOAT32:
            *json_ptr = cJSON_CreateNumber(*(float *)value);
            break;
        case E_OFT_FLOAT64:
            *json_ptr = cJSON_CreateNumber(*(double *)value);
            break;
        case E_OFT_PTR:
            *json_ptr = cJSON_CreateNumber( (int64_t)value);
        case E_OFT_STRUCT:
            *json_ptr = cJSON_CreateObject();
            ret = object_co_json(info->name, value, info->info_ptr, info->info_field_size, *json_ptr, depth);  
            if (ret < 0) {
                printf("%s:%d name=%s object_co_json error. info->type=%d info->mtype=%d\n", __func__, __LINE__, info->name, info->type, info->mtype); 
                cJSON_Delete(*json_ptr);
                return ret;
            }
            break;
        case E_OFT_ARR_STACK:
            if (info->msize == 0) {
                printf("%s:%d name=%s msize error. info->type=%d info->mtype=%d info->msize=%d\n", __func__, __LINE__, info->name, info->type, info->mtype, info->msize); 
                return -1 * depth;
            }
            count = info->size / info->msize;
            if (count == 0) {
                printf("%s:%d name=%s not found info->noffset=%d\n", __func__, __LINE__, info->name, info->noffset);
                break;
            }
            *json_ptr = cJSON_CreateArray();
            
            memcpy(&sub_info, info, sizeof(st_obj_desc_t));
            sub_info.type = info->mtype;
            sub_info.size = info->msize;
            for (int j=0;j<count;j++) {
                sub_obj = value + j * info->msize;
                itemObj = NULL;
                ret = filed_to_json(&sub_info, sub_obj, &itemObj, depth, 0);
                if (ret < 0) {
                    printf("%s:%d name=%s filed_to_json error. info->type=%d info->mtype=%d\n", __func__, __LINE__, info->name, info->type, info->mtype); 
                    return ret;
                }
                cJSON_AddItemToArray(*json_ptr, itemObj);
            }
            break;
        case E_OFT_ARR_HEAP:
            if (arr_count == 0) {
                printf("%s:%d name=%s arr_count == 0. info->type=%d info->mtype=%d\n", __func__, __LINE__, info->name, info->type, info->mtype); 
                return 0;
            }
            memcpy(&sub_info, info, sizeof(st_obj_desc_t));
            sub_info.type = info->mtype;
            sub_info.size = info->msize;
            printf("%s:%d name=%s sub_info.name=%s sub_info.type=%d sub_info.size=%d\n", __func__, __LINE__, info->name, sub_info.name,sub_info.type, sub_info.size);
            *json_ptr = cJSON_CreateArray();
            for (int j=0;j<arr_count;j++) {
                sub_obj = *(void **)value + j * info->msize;
                itemObj = NULL;
                ret = filed_to_json(&sub_info, sub_obj, &itemObj, depth, 0);
                if (ret < 0) {
                    printf("%s:%d name=%s filed_to_json error. info->type=%d info->mtype=%d\n", __func__, __LINE__, info->name, info->type, info->mtype); 
                    return ret;
                }
                cJSON_AddItemToArray(*json_ptr, itemObj);
            }
            break;
        default:
            printf("%s:%d name=%s type error. info->type=%d info->mtype=%d\n", __func__, __LINE__, info->name, info->type, info->mtype); 
            return -1*depth;
        break;
    }
    return 0;
}

int json_to_field(cJSON *root, st_obj_desc_t *info, void* value, int depth, int *arr_count_ptr) 
{
    printf("%s:%d name=%s type=%d arr_count_ptr=%p\n", __func__, __LINE__, info->name, info->type, arr_count_ptr);
    int ret = 0;
    int arr_count = 0;
    int count = 0;
    st_obj_desc_t sub_info;
    cJSON *subJson = NULL;
    switch (info->type)
    {
    case E_OFT_STRING:
        snprintf((char*)value, info->size, "%s", cJSON_GetStringValue(root));
        break;
    case E_OFT_INT8:
        *(int8_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_UINT8:
        *(uint8_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_INT16:
        *(int16_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_UINT16:
        *(uint16_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_INT32:
        *(int32_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_UINT32:
        *(uint32_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_INT64:
        *(int64_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_UINT64:
        *(uint64_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_FLOAT32:
        *(float *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_FLOAT64:
        *(double *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_PTR:
        *(int64_t *)value = cJSON_GetNumberValue(root);
        break;
    case E_OFT_STRUCT:
        ret = json_co_object(info->name, root, value, info->info_ptr, info->info_field_size, depth);
        if (ret < 0) {
            return ret;
        }
        break;
    case E_OFT_ARR_STACK:
        arr_count = *arr_count_ptr = cJSON_GetArraySize(root);
        count = info->size / info->msize;
        if (count < arr_count) {
            printf("%s:%d name=%s not found info->noffset=%d\n", __func__, __LINE__, info->name, info->noffset);
            return -1*depth;
        }
        memset(value, 0, info->size);
        for (int j=0;j<arr_count;j++) {
            subJson = cJSON_GetArrayItem(root, j);
            memcpy(&sub_info, info, sizeof(st_obj_desc_t));
            sub_info.type = info->mtype;
            sub_info.size = info->msize;
            void *sub_obj = value + j * info->msize;
            ret = json_to_field(subJson, &sub_info, sub_obj, depth, 0);
            if (ret < 0) {
                return ret;
            }
        }
        break;
    case E_OFT_ARR_HEAP:
        arr_count = *arr_count_ptr = cJSON_GetArraySize(root);
        if (arr_count == 0) {
            return 0;
        }
        *(void **)value = malloc(arr_count * info->msize);
        memset(*(void **)value, 0, arr_count * info->msize);
        for (int j=0;j<arr_count;j++) {
            memcpy(&sub_info, info, sizeof(st_obj_desc_t));
            sub_info.type = info->mtype;
            sub_info.size = info->msize;
            void *sub_obj = *(void **)value + j * info->msize;
            subJson = cJSON_GetArrayItem(root, j);
            ret = json_to_field(subJson, &sub_info, sub_obj, depth, 0);
            if (ret < 0) {
                return ret;
            }
        }
        break;
    default:
        return -1*depth;
        break;
    }
    return 0;
}

// 对象结构转JSON对象
int object_co_json (const char *name, void *obj, void *obj_desc_ptr, int32_t obj_field_count, void *json_obj, int depth) {
    cJSON *root = (cJSON *)json_obj;
    printf("%s:%d name=%s obj_field_count=%d st_obj_desc_t=%lu depth = %d\n", __func__, __LINE__, name, obj_field_count,  sizeof(st_obj_desc_t), depth);
    int ret = 0;
    int pos = 0;
    for (int i=0;i<obj_field_count;i++)
    {
        st_obj_desc_t *info = obj_desc_ptr+i*(sizeof(st_obj_desc_t));
        if (info->name == NULL) {
            return -1*(depth+i);
        } else if (strcmp("-", info->name) == 0) {
            continue;
        }
        void *ptr = obj+ info->offset;
        int count = 0;
        if (E_OFT_ARR_HEAP == info->type) {
            for (int j=0;j<obj_field_count;j++) {
                st_obj_desc_t *sub_info = obj_desc_ptr+j*(sizeof(st_obj_desc_t));
                if (info->noffset == sub_info->offset) {
                    if (sub_info->size == 1) {
                        count = *(uint8_t*)(obj + sub_info->offset);
                    } else if (sub_info->size == 2) {
                        count = *(uint16_t*)(obj + sub_info->offset);
                    } else if (sub_info->size == 4) {
                        count = *(uint32_t*)(obj + sub_info->offset);
                    } else if (sub_info->size == 8) {
                        count = *(uint64_t*)(obj + sub_info->offset);
                    }
                    break;
                }
                sub_info = NULL;
            }
        }
        cJSON *item = NULL;
        ret = filed_to_json(info, ptr, &item, (depth+i)*10+1, count);
        if (ret < 0 || item == NULL) {
            printf("%s:%d filed_to_json error. ret=%d name=%s field_name=%s filed_type=%d \n", __func__, __LINE__, ret, name,info->name, info->type);
            return ret;
        }
        cJSON *child = NULL;
        if (info->parent_expand == 1 && cJSON_IsObject(root) && cJSON_IsObject(item)) {
            while(cJSON_GetArraySize(item) > 0) {                
                cJSON_ArrayForEach(child, item) {
                    const char *key = child->string;
                    printf("%s:%d merge json obj. ret=%d name=%s field_name=%s filed_type=%d key=%s \n", __func__, __LINE__, ret, name,info->name, info->type, key);
                    cJSON *item_in_target = cJSON_DetachItemFromObjectCaseSensitive(root, key);
                    cJSON *item_in_source = cJSON_DetachItemFromObject(item, key);
                    if (item_in_target != NULL){
                        cJSON_Delete(item_in_target);
                    } 
                    cJSON_AddItemToObject(root, key, item_in_source);
                }
            }
            cJSON_Delete(item);
        } else {
            cJSON_AddItemToObject(root, info->name, item);
        }
    }
    return 0;
}

// JSON对象转对象结构
int json_co_object (const char *name, void *json_obj, void *obj, void *obj_desc_ptr, int32_t obj_field_count,  int depth){
    printf("%s:%d name=%s obj_field_count=%d st_obj_desc_t=%lu depth = %d\n", __func__, __LINE__, name, obj_field_count,  sizeof(st_obj_desc_t), depth);
    cJSON *root = (cJSON *)json_obj;
    int ret = 0;
    for (int i=0;i<obj_field_count;i++)
    {
        st_obj_desc_t *info = obj_desc_ptr+i*(sizeof(st_obj_desc_t));
        if (info->name == NULL) {
            return -1*(depth+i);
        }
        void *ptr = obj+ info->offset;
        cJSON *item = cJSON_GetObjectItem(root, info->name);
        if (info->parent_expand == 1 && cJSON_IsObject(root)) {
            item = root;
        }
        if (item == NULL) {
            continue;
        }
        int count = 0;
        ret = json_to_field(item, info, ptr, (depth+i)*10+1, &count);
        if (ret < 0) {
            return ret;
        }
        if (E_OFT_ARR_HEAP == info->type) {
            for (int j=0;j<obj_field_count;j++) {
                st_obj_desc_t *sub_info = obj_desc_ptr+j*(sizeof(st_obj_desc_t));
                if (info->noffset == sub_info->offset) {
                    if (sub_info->size == 1) {
                        *(uint8_t*)(obj + sub_info->offset) = count;
                    } else if (sub_info->size == 2) {
                        *(uint16_t*)(obj + sub_info->offset) = count;
                    } else if (sub_info->size == 4) {
                        *(uint32_t*)(obj + sub_info->offset) = count;
                    } else if (sub_info->size == 8) {
                        *(uint64_t*)(obj + sub_info->offset) =  count;
                    }
                    break;
                }
                sub_info = NULL;
            }
        }
    }
    return 0;
}

// 对象结构转JSON字符串
int object_co_json_str (const char *name, void *obj, void *obj_desc_ptr, int32_t obj_field_count, char *json_str, uint64_t str_size, int depth) {
    cJSON *json_obj = cJSON_CreateObject();
    int ret = object_co_json(name, obj, obj_desc_ptr, obj_field_count, json_obj, depth);
    if (ret < 0) {
        if (json_obj != NULL) {
            cJSON_Delete(json_obj);
        }
        return ret;
    }
    cJSON_PrintPreallocated(json_obj, json_str, str_size, 0);
    cJSON_Delete(json_obj);
    return 0;
}

// JSON字符串转对象结构
int json_str_co_object (const char *name, char *json_str, uint64_t str_size, void *obj, void *obj_desc_ptr, int32_t obj_field_count,  int depth) {
    cJSON *json_obj = cJSON_ParseWithLength(json_str, str_size);
    if (json_obj == NULL) {
        return -1*depth;
    }
    int ret = json_co_object(name, json_obj, obj, obj_desc_ptr, obj_field_count, depth);
    cJSON_Delete(json_obj);
    return ret;
}

// 获取JSON字符串长度
int get_json_str_size(const char *name, void *obj, void *obj_desc_ptr, int32_t obj_field_count) {
    cJSON *json_obj = NULL;
    int ret = object_co_json(name, obj, obj_desc_ptr, obj_field_count, &json_obj, 0);
    if (ret < 0) {
        if (json_obj != NULL) {
            cJSON_Delete(json_obj);
        }
        return ret;
    }
    cJSON_PrintPreallocated(json_obj, NULL, 0, 0);
    char *json_str = cJSON_GetStringValue(json_obj);
    int len = strlen(json_str);
    cJSON_free(json_str);
    cJSON_Delete(json_obj);
    return len;
}

